package com.world.rickcloudendpoint.algorithm;


import java.util.ArrayList;
/**
 * @description: 导航
 * @author: jlsong
 * @date: 2021/12/27 23:03:52
 * @version: 1.0
 */
public class Navigation {

    //GIS信息点
    private ArrayList<GisPoint> mGISPoints = null;

    //可用的GIS信息列表
    private ArrayList<GisPoint> mAvaliableGisPoint = null;

    public Navigation() {
        mAvaliableGisPoint = new ArrayList<GisPoint>();
        mGISPoints = GisSpace.getRef().cloneGisPoints();
    }

    //导航
    public ArrayList<Point> doNav(Point start, Point end) {
        //如果 gisPoints 没有则返回空
        if (mGISPoints.size() == 0) {
            return new ArrayList<Point>();
        }

        //获取未知点到起点最近的一个 GIS 点
        GisPoint startGP = getClosestGisPoint(start);

        //获取未知点到结束点最近的一个 GIS 点
        GisPoint endGP = getClosestGisPoint(end);

        //起点等于终点
        if (startGP == endGP) {
            ArrayList<Point> retV = new ArrayList<>();
            retV.add(start);
            retV.add(startGP.getCoordinate());
            retV.add(end);
            return retV;
        }

        //添加起点
        mAvaliableGisPoint.add(startGP);

        //初始化 H 值
        for (int i = 0; i < mGISPoints.size(); i++) {
            GisPoint igp = mGISPoints.get(i);
            initGisPointH(igp, endGP);
            igp.setValidate(true);
            igp.setParent(null);
        }

        //获取 F 值最小的 GIS 点
        GisPoint curGP = getMinFGisPoint();

        //标注搜索是否结束
        boolean skip = false;

        //开始搜索
        do {
            //搜索列表为空则退出
            if (mAvaliableGisPoint.size() == 0) {
                break;
            }

            //从可用列表中移除
            removeGisPointFromAvaliableList(curGP);
            curGP.setValidate(false);

            //搜索当前点的连接点
            for (int i = 0; i < curGP.connectionCount(); i++) {
                //获取连接点
                GisPoint newGP = curGP.getConnectionPoint(i);

                //如果搜索已经到了结束点则跳出
                if (newGP == endGP) {
                    skip = true;
                    endGP.setParent(curGP);
                    break;
                }

                //计算新的G值
                double tempG = curGP.getCoordinate().distance(newGP.getCoordinate());
                double newG = tempG + curGP.getG();

                //如果当前点已经失效，则继续下一个点
                if (newGP.isValidate() == false) {
                    continue;
                }

                //当前点已经在可用列表中
                if (isGisPointInAvaliableList(newGP) == true) {
                    if (newGP.getG() < newG) {
                    } else {
                        newGP.setParent(curGP);
                        newGP.setG(newG);
                    }
                } else {
                    newGP.setParent(curGP);
                    newGP.setG(newG);
                    mAvaliableGisPoint.add(newGP);
                }
            }

            //选择 F 值最小的一个 GIS 点,继续搜索
            curGP = getMinFGisPoint();

        }
        while (skip == false);

        //取出点信息
        ArrayList<Point> ret = new ArrayList<Point>();
        getNavPathPoint(endGP, ret);
        return ret;
    }

    //取出 路径的 gis 点
    void getNavPathPoint(GisPoint parent, ArrayList<Point> ret) {
        if (parent == null) return;
        ret.add(parent.getCoordinate());
        if (parent != null) {
            getNavPathPoint(parent.getParent(), ret);
        }

    }

    //获取离某点最近的GIS点
    private GisPoint getClosestGisPoint(Point p) {
        double minDis = 99999999999.0;
        GisPoint ret = null;
        for (int i = 0; i < mGISPoints.size(); i++) {
            GisPoint gp = mGISPoints.get(i);
            double dis = gp.getCoordinate().distance(p);
            if (dis < minDis) {
                minDis = dis;
                ret = gp;
            }
        }
        return ret;
    }

    //点是否在可用列表中
    private boolean isGisPointInAvaliableList(GisPoint p) {
        for (int i = 0; i < mAvaliableGisPoint.size(); i++) {
            GisPoint gp = mAvaliableGisPoint.get(i);
            if (gp == p) {
                return true;
            }
        }
        return false;
    }

    //获取 G 值最小的一个点
    private GisPoint getMinGGisPoint() {
        double minG = 9999999999999.0;
        GisPoint ret = null;
        for (int i = 0; i < mAvaliableGisPoint.size(); i++) {
            GisPoint gp = mAvaliableGisPoint.get(i);
            if (gp.getG() < minG) {
                minG = gp.getG();
                ret = gp;
            }
        }
        return ret;
    }

    //获取 F 值最小的一个点
    private GisPoint getMinFGisPoint() {
        double minF = 9999999999999.0;
        GisPoint ret = null;
        for (int i = 0; i < mAvaliableGisPoint.size(); i++) {
            GisPoint gp = mAvaliableGisPoint.get(i);
            if (gp.getF() < minF) {
                minF = gp.getF();
                ret = gp;
            }
        }
        return ret;
    }

    //从可用列表中移除
    private void removeGisPointFromAvaliableList(GisPoint p) {
        for (int i = 0; i < mAvaliableGisPoint.size(); i++) {
            GisPoint gp = mAvaliableGisPoint.get(i);
            if (gp == p) {
                mAvaliableGisPoint.remove(p);
                return;
            }
        }
    }

    //初始化 H 值
    private void initGisPointH(GisPoint cur, GisPoint endP) {
        double dis = cur.getCoordinate().distance(endP.getCoordinate());
        cur.setH(dis);
    }

}
